/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include "umc_avs_enc_fusion_core.h"
#include "umc_avs_enc.h"
#include "umc_avs_enc_header_init.h"

#include "umc_avs_pic.h"
#include "umc_avs_enc_processing_unit_cpy.h"
#include "umc_avs_enc_processing_unit_enc.h"
#include "umc_avs_dec_processing_unit_deb.h"
#include "umc_avs_enc_processing_unit_me.h"
#include "umc_avs_processing_unit_sleep.h"

#include "umc_video_encoder.h"
#include "umc_automatic_mutex.h"

namespace UMC
{

AVSEncFusionCore::AVSEncFusionCore(void)
{
    m_qp = 12;
} // AVSEncFusionCore::AVSEncFusionCore(void)

AVSEncFusionCore::~AVSEncFusionCore(void)
{
    Release();

} // AVSEncFusionCore::~AVSEncFusionCore(void)

Status AVSEncFusionCore::Release(void)
{
    // call the parent's method
    AVSFusionCore::Release();
    
    // free ME buffer
    if (!m_pMeMemoryChunk)
        ippsFree(m_pMeMemoryChunk);

    return UMC_OK;

} // Status AVSEncFusionCore::Close(void)

#define AVS_ALLOCATE_PROCESSING_UNIT_WITH_PARAMS(name, params) \
    { \
        AVSListElement<AVSProcessingUnit> *pTemp; \
        pTemp = new name(); \
        if (NULL == pTemp) \
            return UMC_ERR_ALLOC; \
        pTemp->m_threadNumber = i; \
        m_pPerformers[i].AddToTail(*pTemp); \
        umcRes = pTemp->Init(&m_guard, params); \
        if (UMC_OK != umcRes) \
            return UMC_ERR_FAILED; \
    }

#define AVS_ALLOCATE_PROCESSING_UNIT(name) \
    { \
        AVSListElement<AVSProcessingUnit> *pTemp; \
        pTemp = new name(); \
        if (NULL == pTemp) \
            return UMC_ERR_ALLOC; \
        pTemp->m_threadNumber = i; \
        m_pPerformers[i].AddToTail(*pTemp); \
        umcRes = pTemp->Init(&m_guard); \
        if (UMC_OK != umcRes) \
            return UMC_ERR_FAILED; \
    }

Status AVSEncFusionCore::Init(Ipp32u numThreads, BaseCodecParams *pParams)
{
    m_pAVSCompressorParams = DynamicCast<AVSVideoEncoderParams> (pParams);
    AVSListElement<AVSProcessingUnit> *pPerf;
    Status umcRes;
    Ipp32u i;
    m_qp = m_pAVSCompressorParams->m_iConstQuant;

    // check error(s)
    if (NULL == m_pAVSCompressorParams)
        return UMC_ERR_NULL_PTR;

    // release the object before initialization
    Release();

    // call the parent's method
    umcRes = AVSFusionCore::Init(numThreads, pParams);
    if (UMC_OK != umcRes)
        return umcRes;

    // initialize the sequence header
    umcRes = InitializeSequenceHeader(&m_seqHeader, (AVSVideoEncoderParams *)m_pAVSCompressorParams);
    if (UMC_OK != umcRes)
        return umcRes;

    // set number of allocated frames.
    // we set any number,
    // because number of frame can be increased during encoding.
    m_maxFrames = IPP_MAX(numThreads, 3);

    // allocates the specific performers
    pPerf = new AVSCpyProcessingUnit();
    if (NULL == pPerf)
        return UMC_ERR_ALLOC;
    m_pPerformers[0].AddToTail(*pPerf);
    umcRes = pPerf->Init(&m_guard);
    if (UMC_OK != umcRes)
        return UMC_ERR_FAILED;

    for (i = 0; i < numThreads; i += 1)
    {
        // allocate the deblocker
        AVS_ALLOCATE_PROCESSING_UNIT(AVSDebProcessingUnit);

        // allocate the encoder(s)
        //AVS_ALLOCATE_PROCESSING_UNIT(AVSEncProcessingUnit);
        AVS_ALLOCATE_PROCESSING_UNIT_WITH_PARAMS(AVSEncProcessingUnit, pParams);

        // allocate the ME unit
        //AVS_ALLOCATE_PROCESSING_UNIT(AVSMEProcessingUnit);
        AVS_ALLOCATE_PROCESSING_UNIT_WITH_PARAMS(AVSMEProcessingUnit, pParams);

        // allocate the sleeper
        AVS_ALLOCATE_PROCESSING_UNIT(AVSSleepProcessingUnit);
    }

    // init MeFrames for ME - Motion Estimation component
    MeAVS* m_Me = new MeAVS;
    MeInitParams MEParamsInit;
    Ipp32s memSizeMe;
    bool me_Res;
    memset(&MEParamsInit,0,sizeof(MEParamsInit));
    MEParamsInit.WidthMB          = (m_pAVSCompressorParams->info.clip_info.width + 15) /16;
    MEParamsInit.HeightMB         = (m_pAVSCompressorParams->info.clip_info.height + 15) /16;
    MEParamsInit.refPadding     = 32;
    MEParamsInit.MbPart         = ME_Mb16x16;

    ////MEParamsInit.SearchDirection = UMC::ME_ForwardSearch;
    MEParamsInit.SearchDirection = UMC::ME_BidirSearch;
    MEParamsInit.MaxNumOfFrame = 8;

    // calculate required size of memory
    me_Res = m_Me->Init(&MEParamsInit, NULL, memSizeMe);
    if(!me_Res)
        return UMC_ERR_ALLOC;
    // allocate required memory for ME
    m_pMeMemoryChunk =  ippsMalloc_8u(memSizeMe);
    memset(m_pMeMemoryChunk, 0, memSizeMe);
    // and init MEParamsInit (really init MeFrames)
    me_Res = m_Me->Init(&MEParamsInit, m_pMeMemoryChunk, memSizeMe);
    // and set pointers
    for(Ipp32s j = 0; j <  MEParamsInit.MaxNumOfFrame; j++)
    {
        m_pMeFramesList[j] = &MEParamsInit.pFrames[j];
    }
    // all MeFrames are free
    m_meMask = 0;
    m_meMaskBFrame = 0;

    return UMC_OK;

} // Status AVSEncFusionCore::Init(Ipp32s iNumThreads, VideoEncoderParams *pParams)

Status AVSEncFusionCore::SetDestination(MediaData *pDst)
{
    AVSListElement<AVSFrame> *pFrame;
    Ipp32u framesToEncode;

    // check error(s)
    if (NULL == pDst)
        return UMC_ERR_NULL_PTR;

    // we can touch frame queue without the synchronization,
    // because we not intend to modify it.
    pFrame = m_Frames.GetHead();
    while ((pFrame) &&
           (pFrame->IsShown()))
    {
        // get the next frame
        pFrame = pFrame->GetNext();
    }

    // we need to check amount of available work to let
    // all threads do their job.
    framesToEncode = IsThereSomethingToEncode(pFrame);
    if (framesToEncode < m_numThreads)
    {
        pFrame = (AVSListElement<AVSFrame> *) 0;
    }
    if (NULL == pFrame)
    {
        return UMC_ERR_NOT_ENOUGH_DATA;
    }

    // save the destination pointer
    pFrame->m_cpyCtx.m_pSrc = pFrame;
    pFrame->m_cpyCtx.m_pDst = pDst;
    switch (pFrame->m_picHeader.PictureType)
    {
    case AVS_I_PICTURE:
        pDst->SetFrameType(I_PICTURE);
        break;
    case AVS_P_PICTURE:
        pDst->SetFrameType(P_PICTURE);
        break;
    case AVS_B_PICTURE:
        pDst->SetFrameType(B_PICTURE);
        break;
    }

    return UMC_OK;

} // Status AVSEncFusionCore::SetDestination(VideoData *pDst)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
